/**
 * @author Zhenjiang.Wang
 * @version 0.1
 */
var Echo = {'version': '0.1'};
/**
 * check if an element is the body element
 * @param {Object} element
 * @returns true|false
 */
function $ifBody(element){
	return (/^(?:body|html)$/i).test(element.tagName);
}
/**
 * check if an object exist & not null & not '' & not exactly 0
 * @param {Object} obj
 * @returns true|false
 */
function $chk2(obj){
	if(obj == undefined) return false;
	if(obj == null) return false;
	if(obj == '') return false;
	return !!(obj || obj === 0)
}
/**
 * check if a string matching a specific type
 * @param {string} chkstr the string to be tested
 * @param {string} type type of the string, maybe int|uint|float|ufloat|date|ym|time|email, ym means year-month
 * @returns true|false
 */
function $chkType(chkstr,type){
	if(!$chk2(chkstr) || !$chk2(type)) return false;
	var trimed = chkstr.trim();
	if(type == 'int') return new RegExp("^-{0,1}[0-9]*$").test(trimed);
	else if(type == 'uint') return new RegExp("^[0-9]*$").test(trimed);
	else if(type == 'float') return new RegExp("^-{0,1}[0-9.]*$").test(trimed);
	else if(type == 'ufloat') return new RegExp("^[0-9.]*$").test(trimed);
	else if(type == 'date') return new RegExp("^\\d{4}-\\d{1,2}-\\d{1,2}$").test(trimed);
	else if(type == 'ym') return new RegExp("^\\d{4}-\\d{1,2}$").test(trimed);
	else if(type == 'time') return new RegExp("^\\d{2}:\\d{2}(:\\d{2})*$").test(trimed);
	else if(type == 'email') return new RegExp("^[a-zA-Z0-9_(-).]{1,}@[a-zA-Z0-9_(-).]{1,}\\.[a-zA-Z0-9_(-).]{1,}$").test(trimed);
}
/**
 * check if a string matching a specific condition
 * @param {string} value the string to be tested
 * @param {string} validator a compound validator, composed of some of required|int|uint|float|ufloat|date|ym|time|email
 * @return {string} the error message, if '', means passed
 */
function $ifValid(value,validator){
	if(validator.indexOf('required')>=0 && value.trim()=='') return '必须输入内容';
	if(validator.indexOf('float')>=0 && !$chkType(value.trim(),'float')) return '必须输入浮点数';
	if(validator.indexOf('int')>=0 && !$chkType(value.trim(),'int')) return '必须输入整数';
	if(validator.indexOf('uint')>=0 && !$chkType(value.trim(),'uint')) return '必须输入正整数';
	if(validator.indexOf('date')>=0 && !$chkType(value.trim(),'date')) return '必须输入日期，如2010-01-01';
	if(validator.indexOf('time')>=0 && !$chkType(value.trim(),'time')) return '必须输入时间，如20:01:01';
	if(validator.indexOf('ym')>=0 && !$chkType(value.trim(),'ym')) return '必须输入年月，如2010-01';
	if(validator.indexOf('email')>=0 && !$chkType(value.trim(),'email')) return '必须输入邮件地址，如abc@123.com';
	return '';
}
/**
 * stop the event bubble to parent object
 * @param {Object} ev the event to be stopped
 */
function $stopBubble(ev){
	if(ev.stopPropagation) ev.stopPropagation();
	ev.cancelBubble = true;
}
/**
 *
 * @param {Object} fileControl
 * @param {Object} imgParent
 */
function $imgPreview(fileControl,imgParent){
	if (Browser.Engine.trident) {
		if (window.XMLHttpRequest) {
			imgParent.innerHTML = '';
			fileControl.select();
			imgParent.filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = document.selection.createRange().text;
		} else {
			imgParent.getElementsByTagName("img")[0].src = fileControl.value;
		}
	} else if (fileControl.files) {
		imgParent.getElementsByTagName("img")[0].src = fileControl.files[0].getAsDataURL();
	}
}
/**
 * dynamic shows the count of characters in a textarea or input
 * @param {Object} textControl the container to be counted
 * @param {Object} tipControl the control to show the count tip
 */
function $calcWord(textControl,tipControl){
    var ctrl = $(textControl);
    if (ctrl) {
		var tc = $(tipControl); if(tc){ tc.innerHTML = ctrl.value.length; }
		ctrl.addEvent('keyup',function(){
			if(tc){ tc.innerHTML = ctrl.value.length; }
		});
    }
}
/**
 * store values carried by checkboxes in the container, every checkbox should have two attributes:
 * id(format:chk_+the value to carry), value(the text to show)
 * @param {Object} container the container
 * @param {Object} textBox the control to show the checked text
 * @param {Object} valueBox the control to store the checked value
 */
function $storeValue(container, textBox, valueBox){
	if (!container) return; container = $(container);
    var ids = ""; var txts = "";
    var childs = container.getElementsByTagName("input");
    for (var i = 0; i < childs.length; i++) {
        if (childs[i].checked) {
            // id格式为chk_+id, 因此,前4个字符略过
            ids += childs[i].id.substring(4) + ","; txts += childs[i].value + "，";
        }
    }
    if (textBox) {
        textBox = $(textBox);
        if (textBox) {
            if (textBox.type == "text" || textBox.type == "hidden") textBox.value = txts;
            else textBox.innerHTML = txts.substring(0, txts.length - 1);
        }
    }
    if (valueBox) { valueBox = $(valueBox); if (valueBox) valueBox.value = ids; }
}
/**
 * show a container with several checkboxes, the check state of these checkboxes determined by the value of the textBox
 * @param {Object} container container to be shown
 * @param {Object} textBox the text box holding values to be checked
 * @param {Object} options extra options, the options are passed to {@link fshow}
 */
function $showInfoTable(container, textBox, options){
    container = $(container); var namestr = ''; if(!options) options = {};
    if (textBox) {
        textBox = $(textBox);
        if (textBox.type == "text" || textBox.type == "hidden") namestr = textBox.value;
        else namestr = textBox.innerHTML;
    }
    var names = namestr.split(",");
	// if has cn char, use cn char as spliter
	if(namestr.indexOf('，') > 0) names = namestr.split('，');
    var type = "checkbox";
    if (options && options.chkType) type = chkType;
    var childs = container.getElementsByTagName("input");
    for (var i = 0; i < childs.length; i++)  childs[i].checked = false;
    for (var i = 0; i < childs.length; i++) {
        if (childs[i].type == type) {
            for (var j = 0; j < names.length; j++) {
                if (names[j] == childs[i].value) childs[i].checked = true;
            }
        }
    }
	options.handle_class = 'title';
	if (Browser.Engine.trident) {
	    var frm = new Element('iframe',{'class':'cover'});
	    container.grab(frm);
    }
	container.fshow(options);
}
/**
 * check or uncheck all checkbox in the specific container, the check state determained by the check all input
 * @param {Object} parent container name or container object
 * @param {Object} chkall the check all input
 */
function $checkAll(parent, chkall){
    var childs = $(parent).getElementsByTagName("input");
    for (var i = 0; i < childs.length; i++) {
		if($chk2(childs[i].type) && childs[i].type=='checkbox')
        	childs[i].checked = $(chkall).checked;
    }
}
/**
 * check one radio box in the container and uncheck the others
 * @param {Object} parent container name or container object
 * @param {Object} self the radio box to be checked
 */
function $selRadio(parent,self){
    var ptable = $(parent);
    var childs = ptable.getElementsByTagName("input");
    for (var i = 0; i < childs.length; i++) {
		if (childs[i] == self)  childs[i].checked = true;
		else childs[i].checked = false;
    }
}
/**
 * get the values of checked checkboxes in the container
 * @param {Object} parent container
 * @param {Object} chkall check all input
 * @returns {string} values splited by ,
 */
function $getCheckValues(parent, chkall){
    var ptable = $(parent); var ids = ""; chkall = $(chkall);
    var childs = ptable.getElementsByTagName("input");
    for (var i = 0; i < childs.length; i++) {
		if($chk2(chkall)){
			if (childs[i].checked && childs[i] != chkall)  ids += childs[i].value + ",";
		}else{
			if (childs[i].checked)  ids += childs[i].value + ",";
		}
    }
    return ids;
}
/**
 * close all MsgBox instances
 * @param {Object} target if specified, close the MsgBox instances in that frame, maybe 'top'|''
 */
function $closeAllMsg(target){
    var boxes = $$('.popup');
	if(target && target=='top') boxes = window.top.$$('div.popup');
    for (var i = 0; i < boxes.length; i++) {
		var iframes = $(boxes[i]).getElements('iframe');
		iframes.each(function(i){i.destroy();});
        boxes[i].destroy();
		// remove all shadows
		var shadows = $(document.body).getElements('div.gShadow');
		shadows.each(function(i){i.destroy()});
    }
}
/**
 * format string, e.g. 'test{0}{1}'.format(var1,var2)
 */
String.prototype.format = function(){
	var args = arguments;
    return this.replace(/\{(\d+)\}/g, function(m,i){return args[i];});
}
String.html2text = function(str){
	// remove the first <p> and the last </p>
	str = str.trim(); if(!str) return "";
	if(str.indexOf('<p>') == 0) str = str.substring(3);
	if(str.indexOf('</p>') == str.length-4) str = str.substring(0,str.length-4);
	return str.replace(/<\/p><p>/g,"\n");
}
String.text2html = function(str){
	return str.replace(/\\n/g,"</p><p>");
}
/**
 * static method to format string, e.g. String.format('test{0}{1}',var1,var2)
 */
String.format = function(){
	if(arguments.length == 0) return null;
    var str = arguments[0];
    for(var i=1;i<arguments.length;i++) {
        var re = new RegExp('\\{' + (i-1) + '\\}','gm');
        str = str.replace(re, arguments[i]);
    }
    return str;
}
/**
 * convert a string to Date object
 */
String.toDate = function(){
	if(arguments.length == 0) return null;
    var str = arguments[0];
	if(!$chkType(str,'date') && !chkType(str,'ym')) return null;
	var dt = new Date(); var times = Date.parse(str); dt.setTime(times);
    return dt;
}
/**
 * encode string using encodeURI
 */
String.encode = function(){
	if(arguments.length == 0) return null;
    var str = arguments[0];
    return encodeURI(str);
}
/**
 * @ignore
 * @param {Object} obj
 * @param {Object} method
 */
function assocEvent(obj,method){
	return (function(e){ e = e || window.event; return obj[method](e,this); });
}
/**
 * @ignore
 * @param {Object} obj
 * @param {Object} method
 */
function assocMethod(obj,method){
	return (function(params){ return obj[method](params,this); });
}
/**
 * @ignore
 */
var DoBase64 = new Class({
    // private property
    _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
	/**
	 * public method for encoding
	 * @param {string} input input string
	 */
    encode : function (input) {
        var output = "", chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;
        input = Base64._utf8_encode(input);
        while (i < input.length) {
            chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++);
            enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);  enc4 = chr3 & 63;
            if (isNaN(chr2)) enc3 = enc4 = 64;
            else if (isNaN(chr3)) enc4 = 64;
            output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
	            this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
        }
        return output;
    },
	/**
	 * public method for decoding
	 * @param {Object} input input string
	 */
    decode : function (input) {
        var output = "", chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;
        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
        while (i < input.length) {
            enc1 = this._keyStr.indexOf(input.charAt(i++)); enc2 = this._keyStr.indexOf(input.charAt(i++));
            enc3 = this._keyStr.indexOf(input.charAt(i++)); enc4 = this._keyStr.indexOf(input.charAt(i++));
            chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;
            output = output + String.fromCharCode(chr1);
            if (enc3 != 64) output = output + String.fromCharCode(chr2);
            if (enc4 != 64) output = output + String.fromCharCode(chr3);
        }
        output = Base64._utf8_decode(output);
        return output;
    },
    // private method for UTF-8 encoding
    _utf8_encode : function (string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";
        for (var n = 0; n < string.length; n++) {
            var c = string.charCodeAt(n);
            if (c < 128) { utftext += String.fromCharCode(c);}
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    },
    // private method for UTF-8 decoding
    _utf8_decode : function (utftext) {
        var string = "", i = 0, c = 0, c1 = 0, c2 = 0;
        while ( i < utftext.length ) {
            c = utftext.charCodeAt(i);
            if (c < 128) { string += String.fromCharCode(c); i++;}
            else if((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }else {
                c2 = utftext.charCodeAt(i+1);
                c3 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    }
});
/**
 * @class static instance to handle base64
 * @example Base64.encode('testdb');
 */
var Base64 = new DoBase64();
/**
 * @class extend Element of mootools, add some methods
 */
Element.implement({
	/**
	 * show a loading text or image hovering over this element
	 * @param {Object} imgUrl url ot the image to show
	 */
	loading: function(options){
		if(this._loadBox) return;
		var coord = this.getCoordinates();
		this._loadBox = new Element('div',{style:'z-index:65534;position:absolute;'});
		document.body.appendChild(this._loadBox);
		var text = 'Loading...';
		if($defined(options)){
			if(options.image){
				var img = new Element('img'); this._loadBox.appendChild(img); img.src = options.image;
			}else{
				this._loadBox.setStyle('background-color','#fff');
				if(options.text) this._loadBox.innerHTML = options.text;
				else this._loadBox.innerHTML = text;
			}
		}
		var selfSize = this._loadBox.getCoordinates();
		var left = coord.left + (coord.width - selfSize.width) / 2;
		var top = coord.top + (coord.height - selfSize.height) / 2;
		this._loadBox.setStyle('left',left); this._loadBox.setStyle('top',top);
		if(Env.Browser.name=='MSIE') window.event.returnValue = false;
	},
	/**
	 * remove the loading box
	 */
	loaded: function(){
		if (this._loadBox) { this._loadBox.close(); this._loadBox = null; }
	},
	/**
	 * get html via an url and fill this element, when loading html, show loading text or image
	 * @param {Object} url the url to be loaded
	 */
	loadUrl: function(url){
		this.loading(); var self = this;
		new echoAjax({url:url,method:'get', onSuccess:function(tree,elements,html,js){
			self.innerHTML = html; self.loaded();
		}});
		if(Env.Browser.name=='MSIE') window.event.returnValue = false;
	},
	loadImg: function(imgurl,loadingImage){
		if($chk2(loadingImage)) this.loading({image: loadingImage});
		else this.loading();
		var self = this;
		var tmp = new Element('img');
		tmp.addEvent('load',function(){ self.src = imgurl; this.destroy(); self.loaded(); });
		tmp.addEvent('error',function(){ this.destroy(); self.loaded(); });
		tmp.src = imgurl;
		if(Env.Browser.name=='MSIE') window.event.returnValue = false;
	},
	/**
	 * set absolute position in the window, no matter the page scrolled or not
	 * @param {Object} obj position object, format {x:0,y:0}
	 */
	setWinPosition: function(obj){
		var wSize = window.getScroll();
		var selfSize = this.getSize();
		var parentPos = {x:0,y:0};
		var parent = this.getOffsetParent();
		while($chk2(parent)) {
			parentPos.x += parent.offsetLeft; parentPos.y += parent.offsetTop; parent = parent.getOffsetParent();
		}
		this.style.top = obj.y + wSize.y + 'px';
		this.style.left = obj.x + wSize.x + 'px';
	},
	/**
	 * get the content rect of this element, removing all padding and margin
	 * @param {Object} parentList as an invisible element has no size in IE,
	 * 		so if the element is invisible, we must make it visible manually and then hide it,
	 * 		this parameter gives the parents to force to be show to make this element visible
	 * @returns {x:width,y:height}
	 */
	getTSize: function(parentList){
		var sz = this.getSize(); var displayList = new Array();
		if($chk2(parentList)){
			for(var i=0;i<parentList.length;i++){
				var parent = parentList[i]; if($chk2(parent)) parent = $(parent);
				if($chk2(parent)){
					var display = parent.style.display; if(!$chk2(display)) display = ''; if(display == 'none') parent.show();
					displayList[i] = display;
				}
			}
		}
		var selfdisplay = this.style.display; if(!$chk2(selfdisplay)) selfdisplay = '';
		if(selfdisplay == 'none'){this.show(); sz = this.getSize(); this.setStyle('display',selfdisplay); }
		else sz = this.getSize();
		if($chk2(parentList)){
			for(var i=0;i<parentList.length;i++){
				var parent = parentList[i]; if($chk2(parent)) parent = $(parent);
				if($chk2(parent) && displayList[i] != '') parent.style.display = displayList[i];
			}
		}
		var border = {	left:this.getStyle('border-left-width').toInt(), right:this.getStyle('border-right-width').toInt(),
						top:this.getStyle('border-top-width').toInt(), bottom:this.getStyle('border-bottom-width').toInt()
					};
		var padding = {	left:this.getStyle('padding-left').toInt(), right:this.getStyle('padding-right').toInt(),
						top:this.getStyle('padding-top').toInt(), bottom:this.getStyle('padding-bottom').toInt()
					};
		sz.x = sz.x - border.left - border.right - padding.left - padding.right;
		sz.y = sz.y - border.top - border.bottom - padding.top - padding.bottom;
		return sz;
	},
	/**
	 * if point in this element
	 * @param {Object} point {x,y}
	 */
	hasPoint: function(point){
		var selfCoord = this.getCoordinates();
		return selfCoord.top<=point.y && selfCoord.bottom>=point.y && selfCoord.left<=point.x && selfCoord.right>=point.x;
	},
	/**
	 * create a shadow cover all the page
	 */
	createShadow: function(){
		var shadow = new Element('div',{'class':'gShadow',style:'z-index:65520;background:#000;filter:alpha(opacity=30);opacity:0.3;width:100%;height:100%;'});
		shadow.setStyle('position','fixed'); shadow.setStyle('left',0); shadow.setStyle('top',0);
		shadow.setStyle('display','none');
		if (Browser.Engine.trident) {
			var winSize = window.getScrollSize();
			shadow.setStyle('position','absolute');
			shadow.setStyle('width',winSize.x); shadow.setStyle('height',winSize.y);
			var frm = new Element('iframe', {'class': 'cover'});
			frm.setStyles({ 'width':winSize.x, 'height':winSize.y });
			shadow.grab(frm);
		}
		$(document.body).appendChild(shadow); shadow.setStyle('display',''); this.shadow = shadow;
	},
	/**
	 * make this element stick to another element
	 * @param options parameter object to specify how to stick
	 * 	<p><b>relativeTo:</b> the object or its name which this element to be sticked to, default is the body element</p
	 * 	<p><b>position:</b> the parameter of mootools Element.Position</p
	 * 	<p><b>edge:</b> the parameter of mootools Element.Position</p
	 * 	<p><b>offset:</b> the {x,y} offset from the relativeTo object</p
	 * 	<p><b>modal:</b> if true, create shadow to cover the page, default is false; if true, must use tHide() to hide this element</p
	 * 	<p><b>allowOutWindow:</b> if true, this element can be out of the window, default is false</p
	 */
	stick: function(options){
		var pos = 'bottomleft'; var edge = 'topleft'; var rel = document.body;
		// if need shadow, do shadow
		if(options && options.modal && !$chk2(this.shadow)) this.createShadow();
		if(options && options.position) pos = options.position;
		if(options && options.edge) edge = options.edge;
		if(options && options.relativeTo) rel = options.relativeTo;
		else if(options && options.event) rel = options.event.target || options.event.srcElement;
		if (rel == document.body) { pos = 'center'; edge = 'center'; }
		this.position({position:pos,edge:edge,relativeTo:rel});
		this.show();
		if(options && options.offset){
			if(options.offset.x) this.style.left = this.getStyle('left').toInt() + options.offset.x + 'px';
			if(options.offset.y) this.style.top = this.getStyle('top').toInt() + options.offset.y + 'px';
		}
		if (!options || !options.allowOutWindow) {
			// see if out of screen, if out, move it in the window
			var winscroll = window.getScroll(); var winsize = window.getSize(); var dim = this.getCoordinates();
			var pos = { x: dim.left, y: dim.top }; var chg_pos = false;
			if (dim.left < 0) { pos.x = 0; chg_pos = true; }
			else if (dim.width < winsize.x && dim.right > winscroll.x + winsize.x) {
					pos.x = winscroll.x + winsize.x - dim.width; chg_pos = true;
			}
			if(dim.top < 0){ pos.y = 0; chg_pos = true; }
			else if (dim.height < winsize.y && dim.bottom > winscroll.y + winsize.y) {
				// make sure the title bar in the window
				if (winsize.y > dim.height) pos.y = winscroll.y + winsize.y - dim.height;
				chg_pos = true;
			}
			if (chg_pos) this.setStyles({left:pos.x,top:pos.y});
		}
		if (Browser.Engine.trident) {
			var frm = new Element('iframe',{'class':'cover'});
			this.setStyle('overflow','hidden');
			this.grab(frm);
		}
	},
	/**
	 * show this element and make it can be moved
	 * @param options parameters to config
	 * 	<p><b>handle - the control to be dragged to move this element</p>
	 * 	<p>all the other parameters in options is passed to stick method {@link stick}</p>
	 */
	fshow: function(options){
		if(!options) options = {};
		var handle = null;
		if(options.handle) handle = options.handle;
		else if(options.handle_class) handle = this.getElement('.' + options.handle_class);
		else handle = this.getElement('.title');
		var rel = document.body;
		if(options.relativeTo) rel = options.relativeTo;
		if(options.event) rel = options.event.target || options.event.srcElement;
		// check if is child of body, if not, move to body
		var parent = this.getParent();
		if(!$ifBody(parent)) $(document.body).grab(this);
		options.relativeTo = rel;
		this.stick(options);
		if($chk2(handle)) var dragg = new Draggable({handle:handle,container:this});
	},
	/**
	 * close the shadow and this element
	 */
	close: function(){
		// remove shadow
		if($chk2(this.shadow)) {
			this.shadow.destroy();
			this.shadow = null;
			try {
				delete this.shadow;
			} catch(e) {}
		}
		this.destroy();
		try {
			delete this;
		} catch(e) {}
	},
	/**
	 * close the shadow and hide this element
	 */
	tHide: function(){
		if($chk2(this.shadow)) {
			this.shadow.destroy();
			this.shadow = null;
			try {
				delete this.shadow;
			} catch(e) {}
		}
		this.hide();
	}
});
/**
 * @ignore
 */
var EnvGetter = new Class({
	Browser:{name:'unknown',version:'unknown',fullname:'unknown',screenSize:'unknown'},
	OS:{name:'unknown'},
	_browserLib:[{name:'Tencent Traveler',key:'TencentTraveler',vkey:'TencentTraveler'},
				{name:'Netscape',key:'Netscape',vkey:'Netscape'},{name:'Chrome',key:'Chrome',vkey:'Chrome\/'},
				{name:'MSIE',key:'MSIE',vkey:'MSIE'},{name:'Firefox',key:'Firefox',vkey:'Firefox\/'},
				{name:'Opera',key:'Opera',vkey:'Version\/'},{name:'Safari',key:'Safari',vkey:'Version\/'}
				],
	_osLib:[{name:'Windows CE',key:'Windows CE'}, {name:'blackberry',key:'blackberry'},
			{name:'android',key:'android'},{name:'iPhone',key:'iPhone'},{name:'iPod',key:'iPod'},
			{name:'palm',key:'palm'},{name:'Windows 98',key:'Windows 98'},
			{name:'Windows NT4.0',key:'Windows NT 4.0'}, {name:'Windows 2000',key:'Windows NT 5.0'},
			{name:'Windows XP',key:'Windows NT 5.1'}, {name:'Windows 2003',key:'Windows NT 5.2'},
			{name:'Windows Vista',key:'Windows NT 6.0'},{name:'Windows 7',key:'Windows NT 6.1'},
			{name:'Ubuntu',key:'Ubuntu'},{name:'Linux',key:'Linux'}, {name:'Mac',key:'Mac'}
			],
	initialize: function(){
		var agent = navigator.userAgent.toLowerCase();
		this.getBrowser(agent);
		this.getOS(agent);
	},
	getBrowser: function(agent){
		for(var i=0;i<this._browserLib.length;i++){
			var item = this._browserLib[i];
			if(agent.indexOf(item.key.toLowerCase())>=0){
				this.Browser.name = item.name;
				// got browser, getting version now
				var re = new RegExp(String.format('.*{0}\\W*([.\\w]+)\\W*.*',item.vkey),'i');
				var version = agent.replace(re,'$1');
				if($chk2(version)) this.Browser.version = version;
				break;
			}
		}
		if(this.Browser.version != 'unknown') this.Browser.fullname = this.Browser.name + this.Browser.version;
		this.Browser.screenSize = String.format('{0}*{1}',window.screen.width,window.screen.height);
	},
	getOS: function(agent){
		for(var i=0;i<this._osLib.length;i++){
			var item = this._osLib[i];
			if(agent.indexOf(item.key.toLowerCase())>=0){
				this.OS.name = item.name; break;
			}
		}
	}
});
/**
 * @class detect browser and operation system
 * @property {Object} Browser {name:'firefox',version:'3.6.3',screenSize:'1024*768'}
 * @property {Object} OS {name:'WindowsXp'}
 */
var Env = new EnvGetter();
/**
 * @class make an object be draggable
 * @param {Object} options
 * <p><b>handle: </b>the control to be dragged to move the container</p>
 * <p><b>container: </b>the control to be moved</p>
 * @example new Draggable({handle:someControl,container:anotherControl});
 */
var Draggable = new Class({
	_options: null, _container: null, _handle: null,
	initialize: function(options){
		this._options = options;
		if(!options.handle) return null;
		this._handle = $(options.handle);
		if(options.container) this._container = $(options.container);
		else if(options.containerCssClass)
			this._container = this.findParent(this._handle,options.containerCssClass);
		if(!this._handle || !this._container) return null;
		this._container.setStyles({'position':'absolute','z-index':'65533'});
		this._handle.onmousedown = assocEvent(this,'doDrag');
	},
	findParent: function(ele,cssClass){
		while(ele && !$(ele).hasClass(cssClass)){
			ele = ele.parentNode;
		}
		if($(ele).hasClass(cssClass)) return ele;
		else return null;
	},
	drag: function(event,handle,container){
        var ev = event || window.event || arguments[0];
        var e = new Event(ev);
        var containerSize = container.getCoordinates();
        var offsetx = e.page.x - containerSize.left;
        var offsety = e.page.y - containerSize.top;
		var doc = document;
		doc.addEvent('mousemove',function(e){
            var newx = e.page.x - offsetx; var newy = e.page.y - offsety;
            $(container).setStyles({left:newx,top:newy});
		});
        doc.addEvent('mouseup',function(){
            doc.removeEvents('mousemove'); doc.removeEvents('mouseup');
        });
	},
	doDrag: function(event,ele){this.drag(event,this._handle,this._container);}
});
/**
 * @class Create a moveable message box, the box can container some controls,too
 * @param {Object} options parameters to make the msgbox
 * <p><b>title: </b>title of the box</p>
 * <p><b>message: </b>show message </p>
 * <p><b>cssClass: </b>cssClass of the box</p>
 * <p><b>style: </b>style text attached to the box</p>
 * <p><b>buttons：</b>buttons to be added to the box[{text:'',action:somefunction}...],
 * 	action can be 'close'(build-in function, means close the box)</p>
 * <p><b>delay: </b>close the box how many milliseconds later</p>
 * <p><b>outClose: </b>if true, the box close when click outside the box, default is false</p>
 * @property close close the box
 * @property smartClose close the box using a fadding effect
 * @property appendButton append a button to the box
 * <p><b>{Object} btnOption</b></p>
 * <p>format:{text:'',action:somefunction},
 * action can be 'close'(build-in function, means close the box)</p>
 * @example new MsgBox({title:'test',message:'msg',buttons:[{text:'close',action:'close'},{text:'test',action:function(){...}}]});
 */
var MsgBox = new Class({
	_minWidth:360, _minHeight:100,
	_options:{},_container: null, _handle: null, _onClose:function(){},
	_title: '信息提示', _message: '', _msgboxClass: 'popup', _sectClass:'',
	_style:'', _btnConfigs:[], _buttons:[], _button_box:null,
	_content_box:null, _shadow_box:null,
	initialize: function(options){
		if(!options) options = {};
		this._options = options;
		if(options.title) this._title = options.title;
		if(options.onClose) this._onClose = options.onClose;
		if(options.message) this._message = options.message;
		if(options.cssClass) this._sectClass = options.cssClass;
		if(options.style) this._style = options.style;
		if(!options.buttons) this._btnConfigs = [{text:'关闭',action:'close'}];
		else this._btnConfigs = options.buttons;
		if($defined(options.autoSize)){ this._minWidth = 0; this._minHeight = 0; }
		else{
			if($defined(options.minWidth)) this._minWidth = options.minWidth;
			if($defined(options.minHeight)) this._minHeight = options.minHeight;
		}
		this.createBox();
		if (options.delay)  this.smartClose.delay(options.delay,this);
		if(options.outClose) {
			var self = this;
			document.addEvent('click',function(){ self.close(); });
			this._container.addEvent('click',function(event){ $stopBubble(event); });
		}
	},
	/**
	 * @ignore
	 */
	createBox: function(){
		var self = this;
		var container  = new Element('div',{'class':this._msgboxClass,style:this._style});
		this._container  = container;
		var inner_box = new Element('div',{'class':'sect'});
		var title_bar = new Element('h2',{style:'zoom:1;'});
		this._handle = new Element('span',{'class':'move title'});
		this._handle.appendText(this._title);
		var close_box = new Element('span',{'class':'extra'});
		var link_a = new Element('a',{'href':'javascript:;','class':'btnClose'});
		link_a.addEvent('click',function(){ self.close(); });
		link_a.appendText('X');
		close_box.appendChild(link_a);
		title_bar.grab(this._handle); title_bar.grab(close_box);
		var sectcontent_box = new Element('div',{'class':'sectContent'});
		if($chk2(this._sectClass)) sectcontent_box.addClass(this._sectClass);
		this._content_box = new Element('div',{'class':'newContent'});
		if(this._message != ''){
			var text_box = new Element('div',{'class':'textBox'});
			text_box.innerHTML = this._message;
			this._content_box.grab(text_box);
		}
		var btn_box = new Element('div',{'class':'sectBtn'});
		this._button_box = btn_box;
		sectcontent_box.grab(this._content_box);
		// buttons
		if(this._btnConfigs){ for(var i=0;i<this._btnConfigs.length;i++) this.appendButton(this._btnConfigs[i]); }
		sectcontent_box.grab(btn_box);
		inner_box.grab(title_bar);
		inner_box.grab(sectcontent_box);
		this._container.grab(inner_box);
		this._container.style.display = "none";

		$(document.body).appendChild(this._container);

		if (this._message != '') {
			var sz = this._container.getTSize();
			// set minHeight and minWidth
			var contSz = this._content_box.getTSize([this._container]);
			if (this._minWidth > 0 && contSz.x < this._minWidth) {
				this._container.setStyle('width', sz.x + this._minWidth - contSz.x);
				this._content_box.setStyle('width', this._minWidth);
			}
			if (this._minHeight > 0 && contSz.y < this._minHeight) {
				this._container.setStyle('height', sz.y + this._minHeight - contSz.y);
				this._content_box.setStyle('height', this._minHeight);
			}
		}
		// show after resize
		this._container.fshow($merge(this._options,{handle:this._handle}));
	},
	refresh: function(){
		this._container.fshow($merge(this._options,{handle:this._handle}));
	},
	setOnClose: function(onclose){
		this._onClose = onclose;
	},
	/**
	 * close the box
	 */
	close: function(){
		this._onClose();
		this._container.close();
	},
	/**
	 * close the box using a fadding effect
	 */
	smartClose: function(){
		var self = this;
		var mfx = new Fx.Morph(self._container,{duration:1000,onComplete:function(){ self.close(); }});
		mfx.start({opacity:[1,0]});
	},
	/**
	 * append a button to the box
	 * @param {Object} btnOption format:{text:'',action:somefunction},
	 * action can be 'close'(build-in function, means close the box)
	 */
	appendButton: function(btnOption){
		if(!btnOption.text) return;
		if (this._buttons.length > 0) {
			var span = new Element('span'); span.appendText(' ');
			this._button_box.grab(span);
		}
		var self = this; var button;
		if(btnOption.type && btnOption.type != ''){
			if(btnOption.type == 'button') button = new Element('button',{'class':'button'});
			else if(btnOption.type == 'link') button = new Element('a',{href:'javascript:;'});
			else button = new Element('button',{'class':'button'});
		}else{ button = new Element('button',{'class':'button'}); }
		button.appendText(btnOption.text);
		if(btnOption.action == 'close') button.addEvent('click',function(){ self.close(); });
		else{
			if($type(btnOption.action) == 'function')
				button.addEvent('click',function(){ (btnOption.action)(); });
		}
		if(btnOption.cssClass) {
			button.addClass(btnOption.cssClass);
		}
		this._buttons[this._buttons.length] = button;
		this._button_box.grab(button);
	},
	appendElement: function(elem){
		if(!$chk2(elem)) return;
		if($type(elem) != 'element') return;
		this._content_box.grab(elem);
		this.refresh();
	},
	getContentSize: function(){
		return this._content_box.getTSize();
	},
	centerText: function(text){
		var size = this.getContentSize();
		// treat one char as 15px by default
		var padding_topdown = (size.y - 15)/8;
		var padding_leftright = size.x/8;
		var div = new Element('div',{style:String.format('padding:{0}px {1}px;',padding_topdown,padding_leftright)});
		div.innerHTML = text;
		this._content_box.empty();
		this.appendElement(div);
	},
	setHtml: function(html){
		this._content_box.innerHTML = html;
	},
	loadPage: function(url){
		var self = this;
		this._content_box.loading({text:'载入中...'});
		new echoAjax({url:url,params:'',method:'get', type:'html',
			onSuccess:function(tree,elements,html,js){
				self._content_box.innerHTML = html;
				self._content_box.loaded();
				self.refresh();
			}}).fire();
	},
	addEvtHandler: function(evtString, evtFunc){
		if(evtString == 'close'){
			this._onClose = evtFunc;
		}
	}
});
/**
 * @class create a moveable confirm box
 * @param {Object} options
 * <p><b>message: </b>the confirm message</p>
 * <p><b>action: </b>: function to do when clicking the confirm button</p>
 *  @property close close the box
 */
var ConfirmBox = new Class({
	_message:'', _action:null, _box:null,
	initialize: function(options){
		this._message = options.message || '';
		var self = this;
		this._action = function(){ (options.action || $empty)(); self.close(); };
		this._title = options.title || '确认提示';
		var box_options = {
			message:this._message, title:this._title, modal:true,
			buttons:[{text:'确定',action:this._action},{text:'取消',action:'close'}]
		};
		this._box = new MsgBox($merge(options,box_options));
	},
	/**
	 * close the confirm box
	 */
	close: function(){ if(this._box != null) this._box.close(); }
});
/**
 * @class create a moveable prompt box
 * @param {Object} options
 * <p><b>title: </b>the confirm message</p>
 * <p><b>action: </b>function to do when clicking the ok button</p>
 * <p><b>controls: </b>controls to be added to the box,
 * format:[type:'select|checkGroup|radioGroup|text|textarea|',title:'',id:'',valid:'',{tip}:'',{cssClass}:'',{items}:[text:'',value:'']],
 * the parameters in {}means optional</p>
 *  @property close close the box
 *  @property getControl get control by id
 */
var PromptBox = new Class({
	_title:'输入提示', _controlConfigs:[], _controls:[], _action:null, _box:null,
	initialize: function(options){
		if(options.controls) this._controlConfigs = options.controls;
		if(options.title) this._title = options.title;
		var self = this;
		if(!options.action) this._action = function(){ self.close(); };
		else this._action = function(){
			var result = self.getResult(); if(result == null) return;
			(options.action)(result); self.close();
		};
		var container = this.createControls();
		var box_options = {
			title:this._title, cssClass:'gForm',
			buttons:[{text:'确定',action:this._action},{text:'取消',action:'close'}]
		};
		this._box = new MsgBox($merge(options,box_options));
		this._box.appendElement(container);
	},
	/**
	 * testdb
	 * @type null
	 */
	createControls: function(){
		var container = new Element('div',{'class':'newContainer'}); var self = this;
		this._controlConfigs.each(function(item,index){
			container.grab(self.createField(item));
		});
		return container;
	},
	createField: function(itemConfig){
		// create row div
		var row = new Element('div',{'class':'row'});
		if(itemConfig.title){
			var label = new Element('label'); label.appendText(itemConfig.title); row.grab(label);
		}
		var control = null;
		if(itemConfig.type == 'select'){
			control = new Element('select');
			if(itemConfig.items){
				itemConfig.items.each(function(item){
					var opt = new Element('option');
					opt.text = item.text; opt.value = item.value;
					try{ control.add(opt,null); }catch(ex){ control.add(opt); }
					if(item.selected) opt.set('selected','true');
				});
			}
		}else if(itemConfig.type == 'checkGroup'){
			control = new Element('div');
			control.addClass('col');
			if(itemConfig.items){
				itemConfig.items.each(function(item){
					var opt = new Element('input',{'type':'checkbox','value':item.value});
					if(item.checked) opt.set('checked','true');
					control.grab(opt); control.appendText(item.text);
				});
			}
		}else if(itemConfig.type == 'radioGroup'){
			control = new Element('select');
			if(itemConfig.items){
				itemConfig.items.each(function(item){
					var opt = new Element('input',{'type':'radio','value':item.value});
					if(item.checked) opt.set('checked','true');
					opt.addEvent('click',function(){$selRadio(control,this);});
					control.grab(opt); control.appendText(item.text);
				});
			}
		}else if(itemConfig.type == 'text'){
			control = new Element('input',{'type':'text','class':'inputbox'});
		}else if(itemConfig.type == 'password'){
			control = new Element('input',{'type':'password','class':'inputbox'});
		}else if(itemConfig.type == 'textarea'){
			control = new Element('textarea',{'rows':5});
		}else{
			control = new Element('input');
		}
		control.set('etype',itemConfig.type);
		if(itemConfig.title) control.set('label',itemConfig.title);
		if(itemConfig.id) control.set('eid',itemConfig.id);
		if(itemConfig.valid) control.set('evalid',itemConfig.valid);
		if($chk2(itemConfig.value)) control.set('value',itemConfig.value);
		if(itemConfig.cssClass) control.addClass(itemConfig.cssClass);
		row.grab(control);
		if(itemConfig.tip){
			var tip = new Element('span',{'style':'margin-left:1em;','class':'hint'});
			tip.appendText(itemConfig.tip);
			row.grab(tip);
		}
		this._controls[this._controls.length] = control;
		return row;
	},
	getControl: function(id){
		for(var i=0;i<this._controls.length;i++){
			var item = this._controls[i];
			if(item.get('eid')==id) return item;
		}
	},
	getResult: function(){
		var result = new Object();
		for(var i=0;i<this._controls.length;i++){
			var item = this._controls[i];
			var id = item.get('eid');
			if(!$chk2(id)) return;
			var type = item.get('etype');
			if(type == 'checkGroup'){
				result[id] = $getCheckValues(item); var valid = item.get('evalid');
				if($chk2(valid) && valid.indexOf('required')>=0){
					// only check required
					if(!new RegExp("[^,]").test(result[id])){
						var name = item.get('label');
						new TipBox({host:item,content:name+'必须选择至少一项',direction:'right'}); return null;
					}
				}
			}else if(type == 'radioGroup'){
				result[id] = $getCheckValues(item); var valid = item.get('evalid');
				if($chk2(valid) && valid.indexOf('required')>=0){
					// only check required
					if(!new RegExp("[^,]").test(result[id])){
						var name = item.get('label');
						new TipBox({host:item,content:name+'必须选择',direction:'right'}); return null;
					}
				}
			}else{
				result[id] = item.get('value'); var valid = item.get('evalid');
				if($chk2(valid)){
					var value = item.get('value'); var res = $ifValid(value,valid);
					if(res != ''){
						var name = item.get('label')||'';
						new TipBox({host:item,content:name+res,direction:'right'}); return null;
					}
				}
			}
		}
		return result;
	},
	close: function(){ if(this._box != null) this._box.close(); }
});
/**
 * @class create a log container
 * @property log write log to log container
 */
var Logger = new Class({
	_container: null, _handle: null, _contentDiv: null, _class: 'popup logger',
	_types:[{'text':'debug','trim':'D'},{'text':'info','trim':'I'},{'text':'warn','trim':'W'},{'text':'error','trim':'E'}],
	_style:'background:#fff;border:1px solid #ccc;',
	initialize: function(){
	},
	doInit: function(){
		var container = this.createBox();
		window.addEvent('scroll',function(){
			var wSize = window.getSize(); var selfSize = container.getSize();
			container.setWinPosition({ x: wSize.x - selfSize.x - 50, y: 50 });
		});
	},
	createBox: function(){
		this._container = new Element('div',{'class':this._class,style:this._style});
		var self = this;
		var title_bar = new Element('h2',{style:'zoom:1;overflow:hidden;padding:3px 6px;border-bottom:solid 1px #ccc;font-size:14px;'});
		this._handle = new Element('span',{style:'margin:4px;float:left;width:70%;cursor:move;'});
		this._handle.appendText('Echo Log'); title_bar.grab(this._handle);
		var extra_box = new Element('span',{'class':'extra',style:'float:right;width:26%;margin-top:3px;cursor:pointer;'});

		this._types.each(function(item){
			var log_box = new Element('span',{'class':item.text,'etype':item.text,'title':'切换'+item.text+'信息'});
			log_box.appendText(item.trim); log_box.addEvent('click',function(){self.toggle(this);});
			extra_box.grab(log_box);
		});
		var hide_box = new Element('span',{'class':'hide','title':'最小化'});hide_box.appendText('-');
		hide_box.addEvent('click',function(){self._container.hide();});
		extra_box.grab(hide_box);
		var close_box = new Element('span', {'class': 'close','title':'关闭'});close_box.appendText('X');
		close_box.addEvent('click',function(){ var cont = self._container; self._container = null; cont.close();});
		extra_box.grab(close_box);

		title_bar.grab(extra_box);

		this._contentDiv = new Element('div',{style:'clear:both;margin:1em;overflow-y:scroll;height:200px;'});
		this._contentDiv.empty();
		this._container.grab(title_bar);
		this._container.grab(this._contentDiv);
		this._container.style.display = "none";

		this._container.setStyle('z-index','65535');
		$(window.top.document.body).appendChild(this._container);
		this._container.fshow({handle:this._handle});
		return this._container;
	},
	buidMsg: function(type,msg){
		var dt = new Date();
		var dtstr = String.format('{0}:{1}:{2}:{3}',dt.getHours()>10?dt.getHours():'0'+dt.getHours(),dt.getMinutes()>10?dt.getMinutes():'0'+dt.getMinutes(),
			dt.getSeconds()>10?dt.getSeconds():'0'+dt.getSeconds(),dt.getMilliseconds());
		return String.format("<p class='{0}'><strong>{1}</strong>: {2}</p>",type, dtstr, msg);
	},
	addLog: function(type,args){
		if(!$chk2(this._container)) this.doInit();
		if(this._container.style.display=='none') this._container.show();
		var msg = '';
		if(args.length == 0) msg = '';
		else{
		    msg = args[0];
		    for(var i=1;i<args.length;i++) {var re = new RegExp('\\{' + (i-1) + '\\}','gm'); msg = msg.replace(re, args[i]); }
		}
		// show box
		if (this._container.style.display != 'block') {
			this._container.style.display = "block";
			var wSize = window.getSize(); var selfSize = this._container.getSize();
			this._container.setWinPosition({ x: wSize.x - selfSize.x - 50, y: 50 });
		}
		this._contentDiv.innerHTML += this.buidMsg(type,msg);
		var scSize = this._contentDiv.getScrollSize(); this._contentDiv.scrollTo(scSize.x, scSize.y);
	},
	toggle: function(obj){
		var type = obj.get('etype'); var self = this;
		if(obj.hasClass('disable')){ obj.removeClass('disable'); this._contentDiv.getElements('.'+type).each(function(item){item.style.display = '';});  }
		else{ obj.addClass('disable'); this._contentDiv.getElements('.'+type).each(function(item){item.style.display = 'none';}); }
	},
	log: function(){ var args = arguments || []; this.addLog('info',args); },
	debug: function(){ var args = arguments || []; this.addLog('debug',args); },
	info: function(){ var args = arguments || []; this.addLog('info',args); },
	warn: function(){ var args = arguments || []; this.addLog('warn',args); },
	error: function(){ var args = arguments || []; this.addLog('error',args); }
});
var log = new Logger();
/**
 * @class handle AJAX in an specific way, return result from server need {result:true|false,message:''}
 * @param options options parameters
 * <p><b>url: </b>the request url</p>
 * <p><b>params: </b>the request parameters</p>
 * <p><b>message: </b>if success, show message.success, if failed, show message.failure, if not defined, show default message</p>
 * <p><b>target: </b>if success, fill target with result.message</p>
 * <p><b>redirect: </b>if success, redirect to this parameter</p>
 * <p><b>redirectFromServer: </b>if success, redirect to result.message from server</p>
 * <p><b>reLoad: </b>if success, reload current page</p>
 * @property fire do this AJAX request
 * @example new echoAjax({url:'/abc/',message:{success:'done'}}).fire();
 */
var echoAjax = new Class({
	_options: {}, _url: '', _params: '', _method:'POST', _type: 'json',
	initialize: function(options){
		this._options = options;
		if(options){
			if(options.type && $type(options.type) == 'string'){
				var type = options.type.toLowerCase(); this._type = type;
				switch(options.type){
					case 'json': this._onSuccess = this.jsonSuccess; break;
					case 'html': this._onSuccess = this.htmlSuccess; break;
					default: this._onSuccess = this.jsonSuccess; break;
				}
			}
		}
		if(options.url) this._url = options.url;
		if(options.method) this._method = options.method;
		if(options.type && $type(options.type) == 'string') this._type = options.type.toLowerCase();
		if(options.params) this._params = options.params;
	},
	fire: function(){
		var this_obj = this;
		if(this._type == 'json'){
			new Request.JSON({
				url: this._url, data: this._params, method:this._method,
				onSuccess: function(response){
					this_obj.jsonSuccess(response);
				}, onFailure: function(response){
					this_obj.failure();
				}
			}).send();
		}else if(this._type == 'html'){
			new Request.HTML({
				url: this._url, data: this._params, method:this._method,
				onSuccess: function(tree,elements,html,js){
					this_obj.htmlSuccess(tree,elements,html,js);
				}, onFailure: function(response){
					this_obj.failure();
				}
			}).send();
		}
	},
	jsonSuccess: function(response){
		if(this._options.onSuccess) this._options.onSuccess(response);
		else if(response.result == true || response.result == 'true'){
			if(this._options){
				if(this._options.message && this._options.message.success){
					new MsgBox({message:this._options.message.success});
				}else if(this._options.target){
					$(this._options.target).innerHTML = response.message;
				}else if(this._options.redirect){
					window.location.href = this._options.redirect;
				}else if(this._options.redirectFromServer){
					window.location.href = response.message;
				}else if(this._options.reLoad){
					window.location.reload();
				}
			}
		}else{
			new MsgBox({message:response.message});
		}
	},
	htmlSuccess: function(tree,elements,html,js){
		if(this._options.onSuccess) this._options.onSuccess(tree,elements,html,js);
		else if(this._options.target){
			$(this._options.target).innerHTML = html;
		}
	},
	failure: function(){
		if (this._options.onFailure) this._options.onFailure();
		else {
			var msg = '获取信息出错';
			if (this._options && this._options.message && this._options.message.failure) {
				msg = this._options.message.failure;
			}
			new MsgBox({ message: msg });
		}
	}
});
/**
 * @class build a table using some data, not completed
 */
var echoTable = new Class({
	_options: null,
	initialize: function(){
		;
	}
});
/**
 * @class a tip box to show some information around a specified control
 * @param options option parameters
 * <p><b>host: </b>the element the box relative to, must be specified</p>
 * <p><b>content: </b>the message to show</p>
 * <p><b>style: </b>inline style for the box in mootools' coding style, e.g. {'width':100,'height':200}</p>
 * <p><b>cssClass: </b>cssClass for the box</p>
 * <p><b>direction: </b>the location relative to host, maybe left|top|right|bottom|hover</p>
 * <p><b>delay: </b>if specified, the box will be auto closed [delay] milliseconds later</p>
 * <p><b>outClose: </b>if true,the box will be closed when clicking outside</p>
 * @property close close the box
 * @property smartClose close the box using a fadding effect
 */
var TipBox = new Class({
	_options: {}, _direction:'bottom',_cssClass:'tipBox', _position: 'bottom', _edge:'top', _container: null, _contentBox:null, _host: null,
	initialize: function(options){
		if(!options || !options.host) return null; var host = $(options.host); if(!host) return null;
		if (host._tipBox) { host._tipBox.close(); }
		this._options = options; this._host = host;
		if(options.cssClass) this._cssClass = options.cssClass;
		if(options.direction) this._direction = options.direction;
		this.parseDirection();
		if(options.position) this._position = options.position;
		if(options.edge) this._edge = options.edge;
		this.createBox();
		this._host._tipBox = this._container;
		if (options.delay)  this.smartClose.delay(options.delay,this);
		if(options.outClose) {
			var self = this;
			document.addEvent('click',function(){ self.close(); });
			this._container.addEvent('click',function(event){ $stopBubble(event); });
		}
	},
	parseDirection: function(){
		var dir = this._direction; var pos = 'bottom'; var edge = 'top';
		if(dir == 'bottom') { pos = 'bottom'; edge = 'top'; }
		else if(dir == 'left') { pos = 'left'; edge = 'right'; }
		else if(dir == 'top') { pos = 'top'; edge = 'bottom'; }
		else if(dir == 'right') { pos = 'right'; edge = 'left'; }
		else if(dir == 'hover') { pos = 'center'; edge = 'center'; }
		this._position = pos; this._edge = edge;
	},
	createBox: function(){
		var _div = new Element('div'); var options = this._options; var self = this;
		_div.addClass(this._cssClass);
		_div.setStyles({'z-index':'65534','position':'absolute'});
		if(options.style) _div.setStyles(options.style);
		var contentBox = new Element('div',{'class':'content'});
		_div.grab(contentBox);
		if(!$chk2(options.remove_close)){
			var closeBox = new Element('div',{'class':'close'});
			closeBox.appendText('X'); closeBox.addEvent('click',function(){ self.close(); });
			_div.grab(closeBox);
		}
		this._contentBox = contentBox;
		if(options.content) contentBox.innerHTML = options.content;
		$(document.body).appendChild(_div);
		this._container = _div;
		this._container.stick($merge(options,{position:this._position,edge:this._edge,relativeTo:this._host}));
	},
	setHtml: function(htmltext){
		this._contentBox.innerHTML = htmltext;
	},
	setChild: function(child){
		if (this._contentBox && child) {
			this._contentBox.empty(); this._contentBox.grab(child);
		}
	},
	close: function(){
		if(this._options.onClose) (this._options.onClose)();
		this._container.close(); this._host._tipBox = null;
	},
	smartClose: function(){
		var self = this;
		var mfx = new Fx.Morph(self._container,{duration:1000,onComplete:function(){ self.close(); }});
		mfx.start({opacity:[1,0]});
	}
});
/**
 * @class show search tips while input in search box, server must accept the condition 'key=xxx', and
 * 	return json format: {items:[{text:'',desc:''}...]}
 * @param options option parameters
 * <p><b>host: </b>the element the box relative to, must be specified</p>
 * <p><b>dataUrl: </b>server url to get tips</p>
 * <p><b>style: </b>inline style for the box in mootools' coding style, e.g. {'width':100,'height':200}</p>
 * <p><b>cssClass: </b>cssClass for the box</p>
 * <p><b>form: </b>form to be sumibted while one item is selected</p>
 * <p><b>onSelect: </b>function to be fired while one item is selected</p>
 */
var SearchTip = new Class({
	_options: {}, _host: null, _form:null, _tipBox: null, _dataUrl: null, _lastWord:'', _solved: true,
	_list:null, _onSelect:null, _cssClass:'searchTip',_selClass:'hover',_lastItems:null,
	initialize: function(options){
		if(!options || !options.host || !options.dataUrl) return null;
		this._options = options; this._host = $(options.host); this._dataUrl = options.dataUrl;
		if(!this._host) return null;
		if(options.cssClass) this._cssClass = $(options.cssClass);
		if(options.form) this._form = $(options.form);
		if(options.onSelect) this._onSelect = options.onSelect;
		this._host.onkeyup = this.createContent.bindWithEvent(this);
	},
	getTipBox: function(){
		if (!$chk2(this._tipBox)) {
			var size = this._host.getSize(); var self = this;
			this._tipBox = new TipBox({
				host: this._host, content: '加载中...', remove_close: true, outClose: true,
				style: { width: size.x + 'px'},cssClass:this._cssClass,
				onClose: function(){ self._tipBox = null; }
			});
		}
		return this._tipBox;
	},
	createContent: function(ev){
		var done = this.doKeyup(ev); if(done) return;
		var dataUrl = this._dataUrl; var value = this._host.value; var self = this;
		if(!$chk2(value)){ this.clear(); return; }
		var tipBox = this.getTipBox();
		if(this._lastWord == value){ self.generateContent(self._lastItems); return; }
		this._lastWord = value;
		var ajax = new Request.JSON({
			url:dataUrl, data: 'key=' + value,
			onSuccess: function(response){
				var items = response.items; if(items.length==0){ self.clear(); }
				self._lastItems = items;
				self.generateContent(items); self._solved = true;
			}
		}).send();
	},
	generateContent: function(items){
		if(!$chk2(items)) return;
		var ul = new Element('ul');
		for(var i=0;i<items.length;i++){
			var item = items[i]; var li = new Element('li'); var textNode = new Element('span',{'class':'text'});
			textNode.appendText(item.text); li.appendChild(textNode);
			if($chk2(item.desc)) {
				var descNode = new Element('span',{'class':'desc'});
				descNode.appendText(item.desc); li.appendChild(descNode);
			}
			li.set('_text',item.text);
			if(i==items.length-1) li.addClass('last');
			ul.appendChild(li);
		}
		this._list = ul; var tipBox = this.getTipBox(); tipBox.setChild(ul);this.attachEvents();
	},
	attachEvents: function(){
		var ul = this._list; if(!$chk2(ul)) return;
		// attach events to ul items
		var items = ul.getElementsByTagName('li'); var self = this;
		if(items.length == 0) return;

		for (var i = 0; i < items.length; i++) {
			items[i].addEvent('mouseover', function(event){
				for (var j = 0; j < items.length; j++) $(items[j]).removeClass(self._selClass);
				this.addClass(self._selClass);
			});
			items[i].addEvent('click', function(event){ self.select(); });
		}
		ul.addEvent('keyup', function(event){ var done = self.doKeyup(event); if(done) return; });
	},
	doKeyup: function(event,source){
		var evt = new Event(event);
		var passKeys = ['enter','up','down','left','right','delete','esc'];
		if(evt.key == 'up'){ this.up(); return true;}
		else if(evt.key == 'down'){ this.down(); return true;}
		else if(evt.key == 'enter'){
			this.select(); return true;
		}
		else if(evt.key == 'esc'){ this.clear(); return true; }
		return false;
	},
	select: function(){
		// get selected item
		if(this._list == null) return;
		var items = this._list.getElements('li');
		var sel_item = null;
		for(var i=0;i<items.length;i++){
			if(items[i].hasClass(this._selClass)){ sel_item = items[i]; break; }
		}
		if($chk2(sel_item)) this._host.value = sel_item.get('_text');
		if($chk2(this._form)) this._form.submit();
		if($chk2(this._onSelect)) (this._onSelect)();
		this.clear();
	},
	up: function(){
		if(this._list == null) return;
		var items = this._list.getElementsByTagName('li');
		if(items.length == 0) return;
		var sel_index = -1;
		for (var i = 0; i < items.length; i++) {
			if (items[i].hasClass(this._selClass)) { sel_index = i; break; }
		}
		if (sel_index == -1) {
			items[items.length-1].addClass(this._selClass); this._host.value = items[items.length-1].get('_text');
		}else if (sel_index == 0){  items[0].removeClass(this._selClass); }
		else{
			items[sel_index].removeClass(this._selClass); items[sel_index-1].addClass(this._selClass);
			this._host.value = items[sel_index-1].get('_text');
		}
	},
	down: function(){
		if(this._list == null) return;
		var items = this._list.getElementsByTagName('li');
		if(items.length == 0) return;
		var sel_index = -1;
		for (var i = 0; i < items.length; i++) {
			if ($(items[i]).hasClass(this._selClass)) { sel_index = i; break; }
		}
		if (sel_index == -1){
			items[0].addClass(this._selClass); this._host.value = items[0].get('_text');
		}else if (sel_index == items.length-1) items[items.length-1].removeClass(this._selClass);
		else{
			items[sel_index].removeClass(this._selClass); items[sel_index+1].addClass(this._selClass);
			this._host.value = items[sel_index+1].get('_text');
		}
	},
	clear: function(){ if($chk2(this._tipBox)) this._tipBox.close(); this._tipBox = null; }
});
/**
 * @class build compound conditions to pass to server
 * @param options option parameters
 * <p><b>controls: </b>condition control array, controls maybe ul|input(text|hidden)</p>
 * <p><i>control element parameters: </i>
 * 	e.g. &lt;ul id='test'&gt;&lt;li&gt;&lt;a evalue='tt'&gt;testa&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 * 	if the link above is clicked, a condition like 'test=tt' will be added into page parameters and send to server,
 * 	and while the page loading, the link will be set selected as to the condition 'test=tt' in page parameters
 * </p>
 * <p><b>submitButtons: </b>submit button array, submit button can manually submit the conditions</p>
 * <p><b>autoFire: </b>once click one control, automatically replace current page using new conditions</p>
 * <p><b>extraParams: </b>extra parameters passed to server</p>
 * @example new ParamsBox({controls:$$('params'),submitButtons:[$('btn')]});
 */
var ParamsBox = new Class({
	_options:{}, _autoFire:true, _controls:[], _submitButtons:[], _extraParams:'',
	initialize: function(options){
		if(!options || !options.controls) return;
		this._options = options;
		this._controls = options.controls; if(this._controls.length==0) return;
		if(options.submitButtons) this._submitButtons = options.submitButtons;
		if(options.autoFire) this._autoFire = options.autoFire;
		if(options.extraParams) this._extraParams = options.extraParams;
		this.attachEvt(); this.loadUrl();
	},
	attachEvt: function(){
		var self = this;
		var uls = this._controls;
		for(var i=0;i<uls.length;i++){
			var lnks = uls[i].getElements('a');
			for(var j=0;j<lnks.length;j++) {
				if(!this._submitButtons.contains(lnks[j]))
					lnks[j].addEvent('click',function(event){ self.checkMe(event); });
			}
		}
		this._submitButtons.each(function(item){
			item.addEvent('click',function(){ self.goUrl(); });
		});
	},
	loadUrl: function(){
		// initial all selections
		var uls = this._controls;
		for(var i=0;i<uls.length;i++) this.setState(uls[i].get('id'));
		// set url parameters
		var search = window.location.search.replace('?','');
		var params = search.parseQueryString();
		for(var item in params){
			this.setState(item,params[item]);
		}
	},
	getOtherUrl: function(obj){
		if(!obj) return ''; obj = $(obj); if(!obj) return ''; var ret = '';
		if (obj.tagName.toLowerCase() != 'input') {
			var links = obj.getElements('a');
			for (var i = 0; i < links.length; i++) {
				if ($(links[i].parentNode).hasClass('active')) {
					ret = links[i].get('eurl'); break;
				}
			}
		}else if(obj.type=='text' || obj.type=='hidden' || obj.type==''){
			ret = obj.get('eurl');
		}
		if(!$chk2(ret)) ret = '';
		return ret;
	},
	getState: function(obj){
		if(!obj) return '-1'; obj = $(obj); if(!obj) return '-1'; var ret = '-1';
		if (obj.tagName.toLowerCase() != 'input') {
			var links = obj.getElements('a');
			for (var i = 0; i < links.length; i++) {
				if ($(links[i].parentNode).hasClass('active')) {
					ret = $(links[i]).get('evalue'); break;
				}
			}
		}else if(obj.type=='text' || obj.type=='hidden' || obj.type==''){
			ret = obj.value;
		}
		return ret;
	},
	setState: function(obj,value){
		if(!obj) return; obj = $(obj); if(!obj) return;
		if(obj.tagName.toLowerCase() != 'input'){
			if(!$chk2(value)) value = '-1';
			value = value + '';
			var links = obj.getElements('a');
			for(var i=0;i<links.length;i++){
				if(links[i].get('evalue') == value) $(links[i].parentNode).addClass('active');
				else $(links[i].parentNode).removeClass('active');
			}
		}else{
			if (obj.type == 'text' || obj.type == 'hidden' || obj.type == '') {
				if($chk2(value)) obj.value = value;
				else obj.value = '';
			}
		}
	},
	checkMe: function(ev){
		ev = ev || window.event;
		var element = ev.target || ev.srcElement;
		var parent = element.parentNode.parentNode;
		var links = parent.getElementsByTagName('a');
		for(var i=0;i<links.length;i++){
			if(links[i] == element) $(links[i].parentNode).addClass('active');
			else $(links[i].parentNode).removeClass('active');
		}
		if(this._autoFire)  this.goUrl();
		ev.returnValue = false;
		if(Env.Browser.name=='MSIE') window.event.returnValue = false;
	},
	goUrl: function(){
		var url = ''; var params = new Object(); var uls = this._controls;
		for(var i=0;i<uls.length;i++){
			var id = uls[i].get('id'); params[id] = this.getState(id);
			if(url=='') url = this.getOtherUrl(id);
		}
		url += '?_';
		for(var i in params){ url += '&' + i + '=' + String.encode(params[i]); }
		url += '&' + this._extraParams; url.toURI().go();
	}
});
/**
 * @class calendar web control
 * @param options option parameters
 * <p><b>host: </b>the host element the datepicker relative to</p>
 * <p><b>onlyMonth: </b>if true, only show month, default is false</p>
 * <p><b>clickClose: </b>if true, must click the close button to close the datepicker,
 * 		if false, auto close the datepicker when click outside, default is false</p>
 * <p><b>event: </b>if auto close, must specify event</p>
 * @example someTextInput.addEvent('focus',function(event){new DatePicker({host:this});});
 */
var DatePicker = new Class({
	_options:{}, host : null, year : 0, month : 0, day : 0, _container:null, _shadow:null,
	_year_span:null, _year_input:null,
	month_names : ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],
	weekday_names : ["日","一","二","三","四","五","六"],
	today_name : "今天", close_name : "关闭", _cssClass:'dtpick',
	_month_table_cssClass: 'monthTable', _week_table_cssClass:'weekTable', _day_table_cssClass:'dayTable',
    initialize : function(options){
    	if(!options || !options.host) return;
    	this.host = $(options.host); this._options = options;
		if(options.onlyMonth) this._options.onlyMonth = true;
		else this._options.onlyMonth = false;
		// close all box
		var now_dtpicks = $$('.' + this._cssClass);
		//// first close the inner shadow cover
		now_dtpicks.each(function(item){
			item.getElements('iframe').each(function(sub){ sub.destroy(); });
			item.destroy();
		});
    	this.createBox();
    	// get value of input, if none, set today
    	var val = this.host.value; var dt = new Date();
    	if($chkType(val,"date") || $chkType(val,"ym")){
    		dt = String.toDate(val); if(dt == null) dt = new Date();
    	}
    	this.year = dt.getFullYear(); this.month = dt.getMonth() + 1; this.day = dt.getDate();
		this.showYear(this.year); this.createShadow();
    },
    createShadow: function(){
    	if (Browser.Engine.trident) {
			var dtSize = this._container.getSize();
	        var frm = new Element('iframe',{'class':'cover'});frm.setStyles({'width':dtSize.x,'height':dtSize.y});
	        this._container.grab(frm); this._shadow = frm;
        }
    },
    createBox : function(){
    	var self = this;
        // paint interface
        var dt_div = new Element("div",{'class':this._cssClass,"style":"position:absolute;display:none;z-index:65534;"});
		dt_div.addClass('popup');
        var dt_table = new Element("table",{'class':this._month_table_cssClass});
        var dt_table_body = new Element("tbody");
        // title
        var year_row = new Element("tr");
		var prev_year_cell = new Element("td",{'events': {
			"click": function(event){ self.showYear(self.year - 1); $stopBubble(event);},
			"mouseover":function(){this.addClass('hover');},
			"mouseout":function(){this.removeClass('hover');}
		}}); prev_year_cell.appendText("<<");
		var year_cell = new Element("td",{'colspan':"2"});
        this._year_span = new Element("a",{'href':"javascript:;",'events':{"click":function(event){self.changeYear();$stopBubble(event);}}});
        this._year_input = new Element("input",{'style':"display:none;",'value':this.year,
        	'events':{"keyup":function(event){self.inputYear(event);},"blur":function(event){self.doChangeYear();}}});
        year_cell.appendChild(this._year_span); year_cell.appendChild(this._year_input);
        var next_year_cell = new Element("td",{'events': {
			"click": function(event){ self.showYear(self.year + 1); $stopBubble(event);},
			"mouseover":function(){this.addClass('hover');},
			"mouseout":function(){this.removeClass('hover');}
		}}); next_year_cell.appendText(">>");
        var today_cell = new Element("td",{'events': {
			"click": function(event){ self.showToday(); $stopBubble(event);},
			"mouseover":function(){this.addClass('hover');},
			"mouseout":function(){this.removeClass('hover');}
		}});
        today_cell.appendText(this.today_name);
        var close_cell = new Element("td",{'events': {
			"click": function(event){ self.close();},
			"mouseover":function(){this.addClass('hover');},
			"mouseout":function(){this.removeClass('hover');}
		}});
        close_cell.appendText(this.close_name);
        year_row.appendChild(prev_year_cell); year_row.appendChild(year_cell);
		year_row.appendChild(next_year_cell);year_row.appendChild(today_cell);
		year_row.appendChild(close_cell);
        dt_table_body.appendChild(year_row);
        // month
        var mrow = new Element("tr");
        for(var i=0;i<12;i++){
        	if(i>0 && i%6==0) { dt_table_body.appendChild(mrow); mrow = new Element("tr");}
        	var cell = new Element("td",{'id':"m_"+i,'month':i+1,
				'events':{"click":function(event){self.selectMonth(this.getAttribute("month"));$stopBubble(event);},
					"mouseover":function(){this.addClass('hover');},
					"mouseout":function(){this.removeClass('hover');}
			}});
        	cell.appendText(this.month_names[i]);
        	mrow.appendChild(cell);
        }
        dt_table_body.appendChild(mrow); dt_table.appendChild(dt_table_body); dt_div.appendChild(dt_table);
        // days
        // only create day table when onlyMonth=false
        if(!this._options.onlyMonth){
	        var day_table = new Element("table",{'class':"dayTable"});
	        var day_table_body = new Element("tbody");
	        var head_day_row = new Element("tr",{'class':"weekTitle"});
	        for(var i=0;i<7;i++){
	        	var cell = new Element("td");
	        	cell.appendText(this.weekday_names[i]);
	        	head_day_row.appendChild(cell);
	        }
	        day_table_body.appendChild(head_day_row);
	        var day_row = new Element("tr");
	        for(var i=0;i<42;i++){
	        	if(i>0 && i%7==0) { day_table_body.appendChild(day_row); day_row = new Element("tr");}
	        	var cell = new Element("td",{'id':"d_"+i,
					'events':{"click":function(event){self.selectDay(this.innerHTML);$stopBubble(event);},
						"mouseover":function(){if(this.innerHTML!='')this.addClass('hover');},
						"mouseout":function(){this.removeClass('hover');}
					}});
	        	day_row.appendChild(cell);
	        }
	        day_table_body.appendChild(day_row);
	        day_table.appendChild(day_table_body);
	        dt_div.appendChild(day_table);
        }
        // append to input
        $(document.body).appendChild(dt_div);
        dt_div.stick({relativeTo: this.host});
        this._container = dt_div;
		if(!this._options.clickClose && this._options.event){
			document.addEvent('click',function(){ self.close(); });
			$stopBubble(this._options.event);
		}
    },
    doChangeYear : function(){
		this._year_span.style.display="block"; this._year_input.style.display="none";
    	var val = this._year_input.value;
    	if($chkType(val,"uint")) this.showYear(val.toInt());
    },
    changeYear : function(){
    	this._year_input.value = this._year_span.innerHTML;
    	this._year_span.style.display="none";this._year_input.style.display="block";
		this._year_input.focus();
    },
    inputYear : function(event){
    	if(event.code == "13" && $chkType(this._year_input.value,'uint')) this.doChangeYear();
    },
    showToday : function(){
    	var dt = new Date();
    	this.year = dt.getFullYear(); this.month = dt.getMonth() + 1; this.day = dt.getDate();
    	this.showYear(this.year);
    },
    showYear : function(year){
    	if($chk2(year) && year >= 0){ this._year_span.innerHTML = year; this.year = year; }
    	else this._year_span.innerHTML = this.year;
        // show month
        this.showMonth(this.month);
    },
    showMonth : function(month){
        if($chk2(month) && month >= 1 && month <= 12) this.month = month;
        for(var i=0;i<12;i++){
            if(i == this.month-1){ $("m_"+i).addClass("selmonth");}
            else{ $("m_"+i).removeClass("selmonth");}
        }
        // write days of the month
        if(!this._options.onlyMonth){
	        var specdt = new Date(); specdt.setFullYear(this.year); specdt.setMonth(this.month-1); specdt.setDate(1);
	        var weekday_first = specdt.getDay();
	        var monthdays = 31;
	        if(this.month == 4 || this.month == 6 || this.month == 9 || this.month == 11){monthdays = 30;}
	        else if(this.month == 2){
	            if(this.leap(this.year)) monthdays = 29;
	            else monthdays = 28;
	        }
	        for(var i=0;i<42;i++){ $("d_" + i).empty();$("d_" + i).removeClass("border"); }
	        for(var i=0;i<monthdays;i++){ var cell = $("d_"+(weekday_first + i)); cell.addClass("border");cell.appendText(i+1); }
	        // show selected day
	        this.showDay(this.day);
        }
    },
    showDay : function(day){
        if($chk2(day) && day >= 0 && day <= 31){
            this.day = day; // check if the month has the day
        }
        var specdt = new Date(); specdt.setFullYear(this.year); specdt.setMonth(this.month-1); specdt.setDate(1);
        var weekday_first = specdt.getDay();
        for(var i=0;i<42;i++){
            if(i == this.day-1+weekday_first){ $("d_"+i).addClass("selday"); }
            else{ $("d_"+i).removeClass("selday"); }
        }
    },
    selectMonth : function(month){
    	this.showMonth(month);
    	if(this._options.onlyMonth == true){
	        // write value into controls of parentdiv
	        var datevalue = this.year + "-" + ((this.month < 10) ? '0' + this.month : this.month);
	        this.host.value = datevalue;
	        this.close();
        }
    },
    selectDay : function(day){
		if(!$chk2(day)) return;
    	this.showDay(day);
        // write value into controls of parentdiv
        var datevalue = String.format('{0}-{1}-{2}',this.year,this.month<10 ? '0'+this.month : this.month,this.day<10 ? '0'+this.day : this.day);
        this.host.value = datevalue;
        this.close();
    },
    leap : function(year){
        var result = false;
        if(year % 4 == 0){
        	if(year % 100 == 0){
                if(year % 400 == 0) result = true;
                else  result = false;
            }else  result = true;
        }else  result = false;
        return result;
    },
	close: function(){
		if($chk2(this._shadow)) this._shadow.destroy();
		if($chk2(this._container)) this._container.destroy();
	}
});
var echoForm = new Class({
	_type:'form', _form:null, _options:{}, _beforeSend:function(){return true;}, _onComplete:function(response){},
	initialize: function(options){
		if(options.form) this._form = document.id(options.form);  if(!this._form) return;
		var self = this; this._options = options;
		if(this._form.tagName == 'form') {
			this._type = 'form';
			this._form.addEvent('submit',function(e){
				e.stop(); e.preventDefault();
				if(Env.Browser.name=='MSIE') window.event.returnValue = false;
				self.send();
			});
		}else{
			this._type = 'other';
			var subtn = this._form.getElement('input[type=submit]');
			if(subtn) subtn.addEvent('click',function(){self.send();});
		}
		if(options.beforeSend) this._beforeSend = options.beforeSend;
		if(options.onComplete) this._onComplete = options.onComplete;
	},
	checkValid: function(){
		var elems = this._form.getElements('input|textarea');
		for(var i=0;i<elems.length;i++){
			var value = elems[i].value; var valid = elems[i].get('evalid');
			if(!$chk2(valid)) continue; var res = $ifValid(value,valid);
			if(res != ''){
				new TipBox({host:elems[i],content:'此项'+res,direction:'right'});
				elems[i].focus();
				return false;
			}else{
				if(elems[i]._tipBox) elems[i]._tipBox.close();
			}
		}
		return true;
	},
	getQuery: function(){
		if(!this._form) return;
		var queryString = [];
		this._form.getElements('input, select, textarea', true).each(function(el){
			if (!el.name || el.type == 'button' || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return;
			var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
				return opt.value;
			}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
			$splat(value).each(function(val){
				if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
			});
		});
		return queryString.join('&');
	},
	send: function(){
		if(!this.checkValid()) return;
		var params = this.getQuery();
		var url = this._form.action ? this._form.action: this._form.get('action');
		var type = this._options.type || 'JSON';
		var method = this._form.get('method') || 'post';
		if(!$chk2(url)) url = window.location.href;
		if(!this._beforeSend()) return;
		new echoAjax({url:url,params:params,method:method,type:type,onComplete:this._onComplete}).fire();
	}
});
/**
 * @class build a list using given objects and store values after select some objects
 * @param options option parameters
 * <p><b>objects: </b>the objects to show, object format:{name,value,desc,url}</p>
 * <p><b>htmlContainer: </b>html container to host the list result</p>
 * <p><b>textContainer </b>container to store text of the selected objects</p>
 * <p><b>valueContainer </b>container to store value of the selected objects</p>
 * <p><b>checkType: </b>select type, maybe 'no|radio|checkbox'</p>
 * <p><b>cellPerRow: </b>how many columns in the list table</p>
 */
var List = new Class({
	_checkType: 'no', _objects:null, _htmlContainer:null, _textContainer:null, _valueContainer:null, _cellPerRow:3,
	initialize: function(options){
		if(!options || !options.objects || !options.htmlContainer) return;
		this._objects = options.objects;
		this._htmlContainer = options.htmlContainer;
		this._textContainer = options.textContainer;
		this._valueContainer = options.valueContainer;
		if(options.checkType) this._checkType = options.checkType;
		if(options.cellPerRow) this._cellPerRow = options.cellPerRow;
		this.createBox();
	},
	createBox: function(){
		var cellPerRow = this._cellPerRow;
		var tb = new Element('table',{'class':'sTable'}); var list = this;
		var header = tb.insertRow(0);
		var hcell = header.insertCell(0);
		hcell.colSpan = cellPerRow;
		hcell.innerHTML = String.format('共找到{0}项',this._objects.length);
		var row;
		for(var i=0;i<this._objects.length;i++){
			if(i % cellPerRow == 0) row = tb.insertRow(tb.rows.length);
			var obj = this._objects[i];
			var cell = row.insertCell(row.cells.length);
			switch(this._checkType){
				case 'no': break;
				case 'radio':
					var chk = new Element('input',{'type':'radio','value':obj.value,'vname':obj.name,'autocomplete':'off'});
					chk.addEvent('click',function(){$selRadio(tb,this);list.setText(this.get('vname')); list.setValue(this.get('value'));});
					cell.appendChild(chk); break;
				case 'checkbox':
					var chk = new Element('input',{'type':'checkbox','value':obj.value,'autocomplete':'off'});
					chk.addEvent('click',function(){var val = $getCheckValues(tb,''); list.setValue(val);});
					cell.appendChild(chk); break;
			}
			var lnk = new Element('a',{'href':obj.url,'title':obj.desc,'target':'_blank'});
			lnk.appendText(obj.name);
			cell.appendChild(lnk);
		}
			while(row.cells.length < cellPerRow){ row.insertCell(row.cells.length);}
		$(this._htmlContainer).appendChild(tb);
	},
	setText: function(text){
		if(this._textContainer && $(this._textContainer)){
			var c = $(this._textContainer);
			if(c.type == 'text' || c.type == 'textarea') c.value = text;
			else c.innerHTML = text;
		}
	},
	setValue: function(val){
		if(this._valueContainer && $(this._valueContainer)){
			var c = $(this._valueContainer);
			if(c.type == 'text' || c.type == 'textarea' || c.type == 'hidden') c.value = val;
			else c.innerHTML = val;
		}
	}
});
/**
 * @class build a list via content got from server
 * @params options option parameters
 * <p><b>url: </b>the objects to show, object format:{name,value,desc,url}</p>
 * <p><b>params: </b>parameters passed to server</p>
 * <p><b>htmlContainer: </b>html container to host the list result</p>
 * <p><b>textContainer </b>container to store text of the selected objects</p>
 * <p><b>valueContainer </b>container to store value of the selected objects</p>
 * <p><b>checkType: </b>select type, maybe 'no|radio|checkbox'</p>
 * <p><b>cellPerRow: </b>how many columns in the list table</p>
 */
var ServerList = new Class({
	_checkType: 'no', _url:'',_params:'', _htmlContainer:null, _textContainer:null, _valueContainer:null, _cellPerRow:3,
	initialize: function(options){
		if (!options || !options.url || !options.htmlContainer) return;
		if (options.checkType) this._checkType = options.checkType;
		this._url = options.url;
		if (options.params) this._params = options.params;
		this._htmlContainer = $(options.htmlContainer);
		if (options.textContainer) this._textContainer = $(options.textContainer);
		if (options.valueContainer) this._valueContainer = $(options.valueContainer);
		if(options.cellPerRow) this._cellPerRow = options.cellPerRow;
	},
	getList: function(){
		var self = this;
		self._htmlContainer.innerHTML = "正在检索...";
		var ajax = new echoAjax({ url: self._url, params: self._params,
			onSuccess: function(response){
				// {result:false/true, items:[{value:$value,name:$name,desc:$desc,url:$url},{}...]}
				if (response.result == "true" || response.result == true) {
					self._htmlContainer.innerHTML = '';
					var items = response.items;
					if (!items || items.length == 0) self._htmlContainer.innerHTML = "没有找到";
					else {
						new List({ objects: items, htmlContainer: self._htmlContainer, textContainer: self._textContainer,
							valueContainer: self._valueContainer, checkType: self._checkType, cellPerRow:self._cellPerRow
						});
					}
				}
				else self._htmlContainer.innerHTML = response.message;
			},
			onFailure: function(){
				new MsgBox({ message: "网络连接超时,请重新查找" });
			}
		}).fire();
	}
}
);
